using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;

namespace net.notdot.Attercop {
    [Serializable()]
    public class SearchResult : EventArgs {
        public string Filename;
        public long Size;
        public byte[] Hash;
        public bool IsDir;

        public SearchResult(string filename, long size, byte[] hash, bool isDir) {
            this.Filename = filename;
            this.Size = size;
            this.Hash = hash;
            this.IsDir = isDir;
        }
    }

    [Serializable()]
    public class SearchResultMessage : Message, UdpMessage {
        public readonly int Id;
        public readonly SearchResult[] Results;

        static SearchResultMessage() { ClientInstance.AddMessageHandler<SearchResultMessage>(); }

        public SearchResultMessage(MessageAddressing addressing, SearchMessage source, IList<SearchResult> results) : base(addressing) {
            this.Id = source.Id;
            this.Results = new SearchResult[results.Count];
            results.CopyTo(this.Results, 0);
        }

        private SearchResultMessage(SearchResultMessage orig, int start, int count) : base(orig.Addressing)
        {
            this.Id = orig.Id;
            this.Results = new SearchResult[count];
            Array.Copy(orig.Results, start, this.Results, 0, count);
        }

        protected SearchResultMessage(MessageReader reader)
            : base(reader)
        {
            reader.BeginRegion();
            reader.ReadByte();  //Version
            this.Id = reader.ReadInt32();
            
            int numResults = reader.ReadInt16();
            this.Results = new SearchResult[numResults];
            for (int i = 0; i < numResults; i++)
            {
                string filename = reader.ReadString();
                long size = reader.ReadInt64();
                bool isDir = reader.ReadBoolean();
                byte[] hash = null;
                if(!isDir)
                    hash = reader.ReadBytes();

                this.Results[i] = new SearchResult(filename, size, hash, isDir);
            }

            reader.EndRegion();
        }

        public override void Serialize(MessageWriter writer)
        {
            base.Serialize(writer);

            writer.BeginRegion();
            writer.Write((byte)0);  //Version
            writer.Write(this.Id);

            writer.Write((Int16)this.Results.Length);
            for (int i = 0; i < this.Results.Length; i++)
            {
                writer.Write(this.Results[i].Filename);
                writer.Write(this.Results[i].Size);
                writer.Write(this.Results[i].IsDir);
                if(!this.Results[i].IsDir)
                    writer.Write(this.Results[i].Hash);
            }

            writer.EndRegion();
        }

        public virtual void Handle(ClientInstance c)
        {
            if (this.m_PeerState != PeerState.Introduced)
                return;
            
            Query q;

            if (c.searches.TryGetValue(Id, out q))
            {
                q.Raise(c, this);
            }
        }

        public override Message[] Split(int numPieces)
        {
            if (Results.Length == 1)
                return null;

            int resultsPerPiece = Results.Length / Math.Min(numPieces, Results.Length);
            int pieces = Results.Length / resultsPerPiece;

            SearchResultMessage[] ret = new SearchResultMessage[pieces];

            for (int i = 0; i < pieces; i++)
            {
                ret[i] = new SearchResultMessage(this, i * resultsPerPiece, Math.Min(resultsPerPiece, Results.Length - (i * resultsPerPiece)));
            }

            return ret;
        }
    }
}
